package com.jeesite.modules.sd.common.util;

import com.jeesite.common.config.Global;
import com.jeesite.common.service.ServiceException;
import com.jeesite.common.web.http.ServletUtils;
import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 验证工具类
 * <br/>
 *
 * @author ：leigq
 * @date ：2019/7/31 22:11
 */
public final class ValidUtils {

    private static final Logger log = LoggerFactory.getLogger(ValidUtils.class);

    /**
     * 针对验证不通过抛 {@link ServiceException} 异常处理，适用于 Service 层业务流程执行、判断需要事务回滚的方法
     */
    public static class Exception {
        private Exception() {
        }

        /**
         * 验证参数是否符合表达式
         * <br/>
         *
         * @param expression 表达式， 如果 expression == true 抛 {@link ServiceException} 异常，
         *                   ServiceException 异常在全局异常处理中处理，以 json 格式返回消息给用户
         * @param errorMsg   错误提示信息
         */
        public static void checkArg(boolean expression, @NonNull Object errorMsg) {
            if (expression) {
                throwException(String.valueOf(errorMsg));
            }
        }


        /**
         * 验证字符串是否为空
         * <br/>
         *
         * @param msg      待验证字符
         * @param errorMsg 错误信息
         */
        public static void isEmpty(String msg, @NonNull Object errorMsg) {
            checkArg(StringUtils.isEmpty(msg), String.valueOf(errorMsg));
        }


        /**
         * 验证集合是否为空
         * <br/>
         *
         * @param collection 待验证集合
         * @param errorMsg   错误信息
         */
        public static <E> void isEmpty(Collection<E> collection, @NonNull Object errorMsg) {
            checkArg(StringUtils.isEmpty(CollectionUtils.isEmpty(collection)), String.valueOf(errorMsg));
        }

        /**
         * 验证集合是否为空
         * <br/>
         *
         * @param map      待验证集合
         * @param errorMsg 错误信息
         */
        public static <K, V> void isEmpty(Map<K, V> map, @NonNull Object errorMsg) {
            checkArg(CollectionUtils.isEmpty(map), String.valueOf(errorMsg));
        }


        /**
         * 验证对象是否为空
         * <br/>
         * Optional的使用参考：<a href='http://www.ibloger.net/article/3209.html'>JAVA8之妙用Optional解决判断Null为空的问题</a>
         * <br/>
         *
         * @param o        待验证的对象
         * @param errorMsg 错误信息
         */
        public static void isNull(Object o, @NonNull Object errorMsg) {
            Optional.ofNullable(o).orElseThrow(() -> new ServiceException(String.valueOf(errorMsg)));
        }

        /**
         * 验证 BigDecimal 是否大于 0
         * <br/>
         *
         * @param num      待验证 BigDecimal
         * @param errorMsg 错误信息
         */
        public static void gtZero(@NonNull BigDecimal num, @NonNull Object errorMsg) {
            checkArg(num.compareTo(BigDecimal.ZERO) <= 0, String.valueOf(errorMsg));
        }


        /**
         * 抛自定义 Service 异常
         *
         * @param errorMsg 错误信息
         */
        private static void throwException(String errorMsg) {
            throw new ServiceException(errorMsg);
        }
    }


    /**
     * 针对验证不通过直接返回 json 响应数据给前端的，适用于 Controller、Service 层业务流程执行、判断事务不需要回滚的方法
     */
    public static class Response {
        private Response() {
        }


        /**
         * 验证参数是否符合表达式
         * <br/>
         *
         * @param expression 表达式 如果 expression == true 抛 {@link ServiceException} 异常
         * @param errorMsg   错误提示信息
         */
        public static void checkArg(boolean expression, @NonNull Object errorMsg) {
            if (expression) {
                writeToResponse(String.valueOf(errorMsg));
            }
        }


        /**
         * 验证字符串是否为空
         * <br/>
         *
         * @param msg      待验证字符
         * @param errorMsg 错误信息
         */
        public static void isEmpty(String msg, @NonNull Object errorMsg) {
            checkArg(StringUtils.isEmpty(msg), String.valueOf(errorMsg));
        }


        /**
         * 验证集合是否为空
         * <br/>
         *
         * @param collection 待验证集合
         * @param errorMsg   错误信息
         */
        public static <E> void isEmpty(Collection<E> collection, @NonNull Object errorMsg) {
            checkArg(CollectionUtils.isEmpty(collection), String.valueOf(errorMsg));
        }

        /**
         * 验证集合是否为空
         * <br/>
         *
         * @param map      待验证集合
         * @param errorMsg 错误信息
         */
        public static <K, V> void isEmpty(Map<K, V> map, @NonNull Object errorMsg) {
            checkArg(CollectionUtils.isEmpty(map), String.valueOf(errorMsg));
        }


        /**
         * 验证对象是否为空
         * <br/>
         * Optional的使用参考：<a href='http://www.ibloger.net/article/3209.html'>JAVA8之妙用Optional解决判断Null为空的问题</a>
         * <br/>
         *
         * @param object   待验证的对象
         * @param errorMsg 错误信息
         */
        public static void isNull(Object object, @NonNull Object errorMsg) {
            checkArg(Objects.isNull(object), String.valueOf(errorMsg));
        }

        /**
         * 验证 BigDecimal 是否大于 0
         * <br/>
         *
         * @param num      待验证 BigDecimal
         * @param errorMsg 错误信息
         */
        public static void gtZero(@NonNull BigDecimal num, @NonNull Object errorMsg) {
            checkArg(num.compareTo(BigDecimal.ZERO) <= 0, String.valueOf(errorMsg));
        }

        /**
         * 将错误信息以 json 格式输出到 Response（前端）
         *
         * @param errorMsg 错误信息
         */
        private static void writeToResponse(String errorMsg) {
            final String renderResult = ServletUtils.renderResult(Global.FALSE, errorMsg, null);
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert servletRequestAttributes != null;
            assert servletRequestAttributes.getResponse() != null;
            try {
                servletRequestAttributes.getResponse().getOutputStream().write(renderResult.getBytes());
            } catch (IOException e) {
                log.info("将错误信息以 json 格式输出到 Response 异常", e);
            }
        }
    }
}
